%{
import Lexico.AnalizadorLexico;
import Mensajes.Mensaje;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Observable;
import TablaDeSimbolos.ElementoTS;
import TablaDeSimbolos.TSimbolo;
import TokensDelLenguaje.*;
%}

%token ASIG MAYOR_IGUAL MENOR_IGUAL DIFERENTE IF ELSE THEN FOR PRINT ULONGINT UINTEGER CONST_UI CONST_UL CADENA IDENTIFICADOR BEGIN END STRUCT


%%
/* El programa consiste de declaraciones y bloque */
programa			:	declaraciones bloque
				|	bloque
				|	declaraciones bloque error {yyerror("Programa: se esperaba fin de archivo luego de bloque principal");}
				|	declaraciones bloque_open {yyerror("Programa: se esperaba \"END\" luego de la ultima sentencia");}
				|	programaInvalido
				;

programaInvalido		:	';' {yyerror("El programa no respeta las sintaxis \"<declaraciones> <bloque>\" ");}
				|	declaraciones {yyerror("El programa no respeta las sintaxis \"<declaraciones> <bloque>\" ");}
				;

tipo_estructura			: 	STRUCT IDENTIFICADOR '{' declaraciones '}' {yyout("Declaracion ESTRUCTURA " + $2.elemento_ts.getToken().getCadena() + " como tipo");}
				|	STRUCT  '{' declaraciones '}' {yyerror("Declaracion ESTUCTURA: falta nombre de la Estructura");}
				|	STRUCT IDENTIFICADOR '[' declaraciones ']' {yyerror("Declaracion ESTRUCTURA : se esperaba {} en lugar de []");}
				|	STRUCT IDENTIFICADOR '(' declaraciones ')' {yyerror("Declaracion ESTRUCTURA : se esperaba {} en lugar de ()");}
				|	STRUCT IDENTIFICADOR error {yyerror("Declaracion ESTRUCTURA: se esperaban llaves");}
				;



/* Aca reconoszo las declaraciones*/
declaraciones                   :	declaracion
				|	declaraciones declaracion
				|	declaraciones error {yyerror("Declaracion: sentencia incorrecta");} declaracion
				;

/* Distintos tipo que se pueden declarar*/
declaracion		:		declaracion_ulongint ';'
				|       declaracion_uinteger ';'
				|		tipo_estructura ';'
				|		IDENTIFICADOR declaracion_estructura ';' {yyout("Declaracion del tipo ESTRUCTURA " + $1.elemento_ts.getToken().getCadena());}
				|		declaracion_ulongint error ';' {yyerror("Declaracion ULONGINT: se esperaba \";\"");}
				;

/* Declaracion de un ULONGINT, ej: ULONGINT a,b,c; */
declaracion_ulongint            :	ULONGINT IDENTIFICADOR {yyout("Declaracion ULONGINT");}
				|	declaracion_ulongint ',' IDENTIFICADOR {yyout("Declaracion ULONGINT");}
				|	declaracion_ulongint ',' {yyerror("Declaracion ULONGINT: se esperaba identificador luego de \",\"");}
				|	ULONGINT {yyerror("Declaracion ULONGINT: se esperaba identificador luego de palabra reservada ULONGINT");}
				;

/* Declaracion de un UINTEGER, ej: UINTEGER a,b,c; */
declaracion_uinteger            :	UINTEGER IDENTIFICADOR {yyout("Declaracion UINTEGER"); $2.elemento_ts.setTipo(ElementoTS.UINTEGER)}
				|	declaracion_uinteger ',' IDENTIFICADOR {yyout("Declaracion UINTEGER");}
				|	declaracion_uinteger ',' {yyerror("Declaracion UINTEGER: se esperaba identificador luego de \",\"");}
				|	UINTEGER {yyerror("Declaracion UINTEGER: se esperaba identificador luego de palabra reservada ULONGINT");}
				;

declaracion_estructura		:	IDENTIFICADOR
				|	declaracion_estructura ',' IDENTIFICADOR
				;


/* Definicion de cuerpo Bloque*/
bloque				:	sentencia
				|	bloque_open END			
				|	bloque_open error {yyerror("Bloque: sentencia incorrecta");} END
				;

/* Apertura de Bloque*/
bloque_open			:	BEGIN
				|	bloque_open bloque
				;

/* Formacion de sentencias */
sentencia			:	asignacion ';'
				|	asignacion {yyerror("Asignacion: se esperaba \";\"");} "\n"
				|	seleccion
				|	iteracion 
				|	impresion ';'
				|	impresion {yyerror("Impresion: se esperaba \";\"");}
				|	asignacion_err ';'
				|	impresion_err ';'
				;

/* Asignacion */
asignacion			:	IDENTIFICADOR ASIG expresion {yyout ("Asignacion");}					
				;

/* Asignacion erronea */
asignacion_err			:	IDENTIFICADOR '-' expresion {yyerror("Asignacion: se esperaba \":=\" en lugar de \"-\"");}
				|	IDENTIFICADOR ':' expresion {yyerror("Asignacion: se esperaba \"=\" luego de \":\"");}
				|	IDENTIFICADOR '=' expresion {yyerror("Asignacion: se esperaba \":\" antes de \"=\"");}
				|	IDENTIFICADOR ASIG error {yyerror("Asignacion: se esperaba expresion luego de \":=\"");}
				|	IDENTIFICADOR error {yyerror("Asignacion: se esperaba \":=\" o \",\" luego de identificador");}
				;

/* Seleccion IF (condicion) THEN bloque ELSE bloque todos con presencia oblgatoria */

seleccion			:	IF condicion THEN  {yyout("--- Seleccion IF ---");} seleccion_else
				|	IF condicion {yyout("--- Seleccion IF ---"); yyerror("Seleccion: Se esperaba THEN luego de condicion");} seleccion_else
				|	IF error {yyerror("Seleccion: se esperaba condicion luego de palabra reservada IF");}
				;

seleccion_else			:	bloque ELSE {yyout("--- Seleccion ELSE ---");} seleccion_fin
				|	ELSE {yyout("--- Seleccion ELSE ---"); yyerror("Seleccion: se esperaba bloque luego de condicion");}
				|	bloque error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba palabra reservada ELSE luego de bloque");}
				|	error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba bloque luego de condicion");}
				;

seleccion_fin			:	bloque {yyout("--- Fin de seleccion ---");}
				|	error {yyout("--- Fin de seleccion ---"); yyerror("Seleccion: se esperaba bloque luego de palabra reservada ELSE");}
				;

/* Elemento identificador o CONST_ULante */
entero				:	IDENTIFICADOR
				|	CONST_UL
				|	CONST_UI
				|	elemento_estructura
				;

/* Iteracion */
iteracion			:	FOR '(' entero ASIG entero ';' entero '<' entero ';' entero ')' {yyout("ITERACION ascendente");} bloque {yyout("Fin de Bloque FOR");}
				|	FOR '(' entero ASIG entero ',' entero '<' entero ',' entero ')' {yyerror("ITERACION ascendente: se esperaba \";\" en lugar de \",\"");}
				|	FOR '(' entero ASIG entero ';' entero '>' entero ';' entero ')' {yyerror("ITERACION ascendente: se esperaba \"<\" en lugar de \">\"");}  
				| 	FOR '(' error ')' bloque {yyerror("Iteracion ascendente: declaracion de sentencia FOR no valida" );}
                                ;

/* Impresion PRINT (cadena) */
impresion			:	PRINT '(' CADENA ')' {yyout("Impresion (" + $3.elemento_ts.getToken().getCadena() + ")");}
				;

impresion_err			:	PRINT '[' CADENA ']' {yyerror("Impresion: se esperaba \"(\" y \")\" en lugar de \"[\" y \"]\"");}
				|	PRINT '{' CADENA '}' {yyerror("Impresion: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	PRINT '(' CADENA error {yyerror("Impresion: se esperaba \")\" luego de cadena");}
				|	PRINT '(' error {yyerror("Impresion: se esperaba cadena luego de \"(\"");}
				|	PRINT error {yyerror("Impresion: se esperaba \"(\" luego de palabra reservada PRINT");}
				;

/* Acceso elemento estructura */
elemento_estructura		:	IDENTIFICADOR '.' campo_estructura
				;

/* Acceso a elementos de estructuras de mas de un nivel */
campo_estructura		:	IDENTIFICADOR
				|	campo_estructura '.' IDENTIFICADOR
				;
/* Expresiones Aritmeticas */
expresion			:	termino
				|	expresion '+' termino
				|	expresion '-' termino
				|	expresion '+' error {yyerror("Expresion: se esperaba un termino luego de \"+\"");}
				|	expresion '-' error {yyerror("Expresion: se esperaba un termino luego de \"-\"");}
				;

termino				:	factor
				|	termino '*' factor
				|	termino '/' factor
				|	termino '*' error {yyerror("Termino: se esperaba un factor luego de \"*\"");}
				|	termino '/' error {yyerror("Termino: se esperaba un factor luego de \"/\"");}
				;

factor				:	IDENTIFICADOR
				|	constante
				|	elemento_estructura
				|	'(' expresion ')'
				|	'{' expresion '}' {yyerror("Factor: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	'(' expresion error {yyerror("Factor: se esperaba \")\" luego de expresion");}
				|	'(' error {yyerror("Factor: se esperaba expresion luego de \"(\"");}
				;

constante			:	CONST_UL
				|	CONST_UI
				;


/* Condicion Logica */
condicion			:	'(' expresion operador_logico expresion ')'
				|	expresion operador_logico expresion {yyerror("Condicion: se esperaba \"(\" y \")\"");}
				|	'{' expresion operador_logico expresion '}' {yyerror("Condicion: se esperaba \"(\" y \")\" en lugar de \"{\" y \"}\"");}
				|	'(' expresion operador_logico expresion error {yyerror("Condicion: se esperaba \")\" luego de expresion");}
				|	'(' expresion operador_logico error {yyerror("Condicion: se esperaba expresion luego de operador logico");}
				|	'(' expresion error {yyerror("Condicion: se esperaba operador logico");}
				|	'(' error ')' {yyerror("Condicion: condicon logica no valida");}
                                ;
/*Operador Logico*/
operador_logico			:	'>'
				|	'<'
				|	'='
				|	MAYOR_IGUAL
				|	MENOR_IGUAL
				|	DIFERENTE
				;



%%

AnalizadorLexico al;    // Analizador Lexico
TSimbolo ts;            // Tabla de Simbolos

public Parser(AnalizadorLexico al, TSimbolo ts)
{
  this.al = al;
  this.ts = ts;
}

void yyout(String texto) {
    Mensaje msj = new Mensaje("linea " + al.get_num_lineas() + " - " + texto, Mensaje.SENT);
    setChanged();
    notifyObservers(msj);
}

void yyerror(String texto) {
    Mensaje msj = new Mensaje("ERROR SINTACTICO: linea " + al.get_num_lineas() + " - " + texto, Mensaje.INF);
    setChanged();
    notifyObservers(msj);
}


short yylex() throws FileNotFoundException, IOException{
    Token token = al.obtener_token();
    Mensaje msj = new Mensaje(token.toString()+"\n", Mensaje.TOKEN);
    setChanged();
    notifyObservers(msj);
    yylval = new ParserVal(ts.get(token));
    return token.traducir();
}


Token getToken(ParserVal val){
    return val.elemento_ts.getToken();
}
